package oleg.locale;


/**
 * Copyright 2009 Oleg Frenkel
 *
 * This file is part of PerfectSearch.
 *
 * PerfectSearch is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * PerfectSearch is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with PerfectSearch.  If not, see <http://www.gnu.org/licenses/>.
 */

import java.util.Hashtable;
import java.util.Vector;

import oleg.C;
import oleg.MainForm;
import oleg.cfg.Config;
import oleg.lcdui.InputTextProcessor;
import oleg.log.Logger;
import oleg.util.Arrays;
import oleg.util.Collections;

public class StringNormalizer
{
  static private final StringNormalizer instance = new StringNormalizer();
  static private final String[] EMPTY_NORM_RULE = new String[]{};

  private Config cfg = Config.instance();
  private String locale = null;

  // Every item contains exactly three characters. First character is to
  // be normalized, second character is its equivalent character, and
  // third character is a corresponding digit on the cell key pad (which 
  // is the first character's equivalent character in "prediction on" mode).
  // All string items are sorted, so binary search could be used.
  // If character does not appear in rules, its equivalent character is the same
  // for both "prediction on" and "prediction off" modes
  private String[] normRules = EMPTY_NORM_RULE;

  // This is singleton
  private StringNormalizer() {}

  static public StringNormalizer instance(String locale) {
    if (locale.equals(instance.locale)) {
      return instance;
    }

    instance.loadLocale(locale);
    return instance;
  }
  
  // Return a normalized string or null if string does not match the pad mode
  public String normalize(String str) {
    StringBuffer normStr = new StringBuffer(str.length());
    for (int i = 0; i < str.length(); i++) {
      char curChar = str.charAt(i);
      if (cfg.getPadMode() == C.PAD_MODE_PRED_ON && Character.isDigit(curChar)) {
        return null;
      }

      normStr.append(normalize(curChar));
    }
    return normStr.toString();
  }

  public char normalize(char c) {
    String s = new String(new char[]{c});
    int index = -1 * Arrays.binarySearchString(normRules, s);

    // We should check index-1, index and index+1
    String curNormRule = null;
    
    if (index -1 >= 0 && index -1 < normRules.length && normRules[index - 1].charAt(0) == c) {
      curNormRule = normRules[index - 1];
    } else if (index >= 0 && index < normRules.length && normRules[index].charAt(0) == c) {
      curNormRule = normRules[index];
    } else if (index + 1 >= 0 && index + 1 < normRules.length && normRules[index + 1].charAt(0) == c) {
      curNormRule = normRules[index + 1];
    }

    if (curNormRule == null) {
//       Logger.log("StringNormalizer: converting " + c + " to " + c);
      return c;
    }

//     Logger.log("StringNormalizer.normalize: c: " + c +
//                " index: " + index +
//                " normRule at index: " + curNormRule);
    if (curNormRule.charAt(0) != c) {
      return c;
    }

    int padMode = cfg.getPadMode();
    int row = padMode == C.PAD_MODE_PRED_ON ? 2 : 1;
//     Logger.log("converting " + c + " to " + curNormRule.charAt(row));
    return curNormRule.charAt(row);
  }

  private void loadLocale(String newLocale) {
    if (newLocale.equals(locale)) {
      return;
    }

    locale = newLocale;
    normRules = EMPTY_NORM_RULE;   

    // Load chars layout on key pads
    String[][] chars = new String[10][];
    InputTextProcessor.fillCharNumpadLayout(chars, locale);
    Hashtable charsToKeyHT = new Hashtable(40);
    for (int i = 2; i <= 9; i++) {
      for (int j = 0; j < chars[i].length; j++) {
        charsToKeyHT.put(chars[i][j], "" + i);
      }
    }
//     System.out.println("StringNormalizer: charsToKeyHT: " + charsToKeyHT); 

    // Load locale
    LocaleManager localeMgr = LocaleManager.instance();
    String normRulesRawStr = localeMgr.getProperty(locale, C.STRING_NORMALIZER);
    if (normRulesRawStr.equals(".")) {
      return;
    }

//     Logger.log("StringNormalizer: normRulesRawStr: " + normRulesRawStr);

    boolean secondCharReady = false;
    char secondChar = 0;
    Vector normRulesV = new Vector();
    StringBuffer curItem = new StringBuffer(3);
    for (int i = 0; i < normRulesRawStr.length(); i++) {
      char curChar = normRulesRawStr.charAt(i);
      if (curChar == '.') {
        secondCharReady = false;
        continue;
      }
      if (!secondCharReady) {
        secondChar = curChar;
        secondCharReady = true;
      }
    
      curItem.delete(0, curItem.length());
      curItem.append(curChar);
      curItem.append(secondChar);
      curItem.append((String)charsToKeyHT.get("" + secondChar));
      normRulesV.addElement(curItem.toString());
    }

    Collections.sortString(normRulesV);
//     System.out.println("StringNormalizer: normRulesV: " + normRulesV); 
    normRules = new String[normRulesV.size()];
    normRulesV.copyInto(normRules);
//     Arrays.sort(normRules);
//     Logger.log("StringNormalizer: normRules: " + normRulesV);

  }

}